package com.pzh.usercenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pzh.usercenter.common.ErrorCode;
import com.pzh.usercenter.exception.BusinessException;
import com.pzh.usercenter.mapper.TeamMapper;
import com.pzh.usercenter.model.domain.Team;
import com.pzh.usercenter.model.domain.User;
import com.pzh.usercenter.model.domain.UserTeam;
import com.pzh.usercenter.model.dto.TeamQuery;
import com.pzh.usercenter.model.enums.TeamStatusEnum;
import com.pzh.usercenter.model.request.JoinTeamRequest;
import com.pzh.usercenter.model.request.QuitTeamRequest;
import com.pzh.usercenter.model.request.TeamUpdateRequest;
import com.pzh.usercenter.model.vo.TeamUserVO;
import com.pzh.usercenter.model.vo.UserVO;
import com.pzh.usercenter.service.TeamService;
import com.pzh.usercenter.service.UserService;
import com.pzh.usercenter.service.UserTeamService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
* @author fc3379757631
* @description 针对表【team(队伍)】的数据库操作Service实现
* @createDate 2025-04-06 19:22:07
*/
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
    implements TeamService {

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addTeam(Team team, User loginUser) {
        //1.请求参数是否为空？
        if(team == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2.是否登录，未登录不允许创建
        if(loginUser == null){
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        final Long userId = loginUser.getId();
        //3.校验信息
        //i.队伍人数>1且<=20
        //包装可能为 null 的值, 如果为 null，返回默认值 0
        int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if(maxNum < 1 || maxNum > 20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍人数不满足要求");
        }
        //ii.队伍标题<=20
        String teamName = team.getName();
        if(StringUtils.isBlank(teamName) || teamName.length() > 20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍名称不满足要求");
        }
        //iii.描述<=512
        String description = team.getDescription();
        if(StringUtils.isNotBlank(description) && description.length() > 512){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍描述过长");
        }
        //iv.status是否公开(int)不传默认为0（公开）
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if(statusEnum == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍状态异常");
        }
        //v.如果status是加密状态，一定要有密码，且密码<=32
        String password = team.getPassword();
        if(TeamStatusEnum.ENCRYPT.equals(statusEnum)){
            if(password == null || password.length() > 32){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码设置错误");
            }
        }
        //vi.超时时间>当前时间
        Date expireTime = team.getExpireTime();
        if(new Date().after(expireTime)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"超时时间 > 当前时间");
        }
        //vii.校验用户最多创建5个队伍
        // todo 有bug ，用户可能同时创建100个队伍
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long count = this.count(queryWrapper);
        if(count >= 5){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户最多创建5个队伍");
        }

        //4.满足所有要求，插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(userId);
        boolean save = this.save(team);
        Long teamId = team.getId();
        if(!save || teamId == null){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"创建队伍失败");
        }
        //5.插入用户=>队伍关系到关系表

        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(teamId);
        userTeam.setUserId(userId);
//        userTeam.setId(null);
        userTeam.setCreateTime(new Date());
        boolean saveUserTeam = userTeamService.save(userTeam);
        if(!saveUserTeam){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"插入失败");
        }
        return teamId;
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery,boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // todo 查询语句可以进一步优化（lambdaQueryWrapper）
        //1.从请求参数中取出队伍名称等查询条件，如果存在则作为查询条件
        Long id = teamQuery.getId();
        if(id != null && id > 0){
            queryWrapper.eq("id", id);
        }

        //将多个 id 作为条件
        List<Long> idList = teamQuery.getIdList();
        if(!CollectionUtils.isEmpty(idList)){
            queryWrapper.in("id", idList);
        }

        //2.可以通过某个关键词同时对名称和描述查询
        String searchText = teamQuery.getSearchText();
        if(StringUtils.isNotBlank(searchText)){
            queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
        }
        //队伍名称
        String name = teamQuery.getName();
        if(StringUtils.isNotBlank(name)){
            queryWrapper.like("name", name);
        }
        //描述
        String description = teamQuery.getDescription();
        if(StringUtils.isNotBlank(description)){
            queryWrapper.like("description", description);
        }
        //根据最大人数查询
        Integer maxNum = teamQuery.getMaxNum();
        if(maxNum != null && maxNum > 0){
            queryWrapper.eq("maxNum", maxNum);
        }
        //根据创建人查询
        Long userId = teamQuery.getUserId();
        if(userId != null && userId > 0){
            queryWrapper.eq("userId", userId);
        }
        //根据状态查询
        Integer status = teamQuery.getStatus();
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if(statusEnum == null){
            statusEnum = TeamStatusEnum.PUBLIC;
        }
        //3.只有管理员才能查看加密还有非公开的房间
        if(!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        queryWrapper.eq("status", statusEnum.getStatus());


        //4.不展示已过期的队伍（根据过期时间筛选）
        queryWrapper.and(qw -> qw.gt("expireTime",new Date()).or().isNull("expireTime"));

        //5. todo 关联查询已加入队伍的用户信息（可能会很耗费性能，建议大家用自己写SQL的方式实现）

        List<Team> list = this.list(queryWrapper);
        if(CollectionUtils.isEmpty(list)){
            return new ArrayList<>();
        }

        //6.返回
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : list) {
            Long teamUserId = team.getUserId();
            if(teamUserId == null){
                continue;
            }
            User user = userService.getById(teamUserId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team,teamUserVO);
            //脱敏用户信息

            if(user != null){
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user,userVO);
                teamUserVO.setCreateUser(userVO);
            }

            teamUserVOList.add(teamUserVO);
        }

        return teamUserVOList;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        //1.判断请求参数是否为空
        if(teamUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2.查询队伍是否存在
        Long id = teamUpdateRequest.getId();
        if(id == null || id <= 0){
            throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在！");
        }
        //3.只有管理员或者队伍的创建者可以修改
        if(!userService.isAdmin(loginUser) && teamUpdateRequest.getUserId() != loginUser.getId()){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //4.如果用户传入的新值和老值一致，就不用update了（可自行实现，降低数据库使用次数）
        Team oldTeam = this.getById(teamUpdateRequest.getId());
        if(oldTeam.getName().equals(teamUpdateRequest.getName())
                && oldTeam.getDescription().equals(teamUpdateRequest.getDescription())
//                && Objects.equals(oldTeam.getMaxNum(), teamUpdateRequest.getMaxNum())
                && Objects.equals(oldTeam.getUserId(), teamUpdateRequest.getUserId())
                && Objects.equals(oldTeam.getStatus(), teamUpdateRequest.getStatus())
                && Objects.equals(oldTeam.getExpireTime(), teamUpdateRequest.getExpireTime())
                && Objects.equals(oldTeam.getPassword(), teamUpdateRequest.getPassword())){
            return true;
        }
        //5.如果队伍状态改为加密，必须要有密码
        Integer status = teamUpdateRequest.getStatus();
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if(statusEnum.equals(TeamStatusEnum.ENCRYPT)){
            if(StringUtils.isBlank(teamUpdateRequest.getPassword())){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"加密房间必须有密码");
            }
        }
        //6.更新成功
        Team team = new Team();
        BeanUtils.copyProperties(teamUpdateRequest,team);
        return this.updateById(team);
    }

    @Override
    public boolean joinTeam(JoinTeamRequest joinTeamRequest, User loginUser) {
        if(joinTeamRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //1.队伍必须存在，只能加入未满、未过期的队伍
        Long teamId = joinTeamRequest.getTeamId();
        if(teamId == null || teamId <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }
        Team team = this.getById(teamId);
        Date expireTime = team.getExpireTime();
        if(expireTime != null && expireTime.before(new Date())){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已过期");
        }

        //2.禁上加入私有的队伍
        Integer status = team.getStatus();
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if(TeamStatusEnum.PRIVATE.equals(statusEnum)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"禁止加入私有队伍");
        }

        //3.如果加入的队伍是加密的，必须密码匹配才可以
        String password = joinTeamRequest.getPassword();
        if(TeamStatusEnum.ENCRYPT.equals(statusEnum)){
            if(StringUtils.isBlank(password) || !password.equals(team.getPassword())){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码错误");
            }
        }

        //集群时使用synchronized 有问题 ，使用分布式锁
        RLock lock = redissonClient.getLock("yupao:join_team");
        try {
            //每天所有线程尝试一次获取锁，等待0s，拿到执行，否则放弃
            // -1 ，看门狗机制，每10s续期一次
            while(true){
                if (lock.tryLock(0,-1, TimeUnit.SECONDS)) {
                    //4.用户最多加入5个队伍
                    QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userId", loginUser.getId());
                    long count = userTeamService.count(queryWrapper);
                    if(count > 5){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"一个用户最多加入5个队伍");
                    }

                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("teamId", joinTeamRequest.getTeamId());
                    count = userTeamService.count(queryWrapper);
                    if(count >= team.getMaxNum()){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已满");
                    }

                    //5.不能重复加入已加入的队伍（幂等性）
                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userId", loginUser.getId());
                    queryWrapper.eq("teamId", joinTeamRequest.getTeamId());
                    count = userTeamService.count(queryWrapper);
                    if(count > 0){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已加入该队伍");
                    }

                    //修改队伍信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(loginUser.getId());
                    userTeam.setTeamId(teamId);
                    //6.新增队伍-用户关联信息
                    return userTeamService.save(userTeam);
                }
            }


        } catch (InterruptedException e) {
            log.error("redis lock error",e);
            return  false;
        } finally {
            //只能释放自己的锁
            if(lock.isHeldByCurrentThread()){
                System.out.println("unlock" + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(QuitTeamRequest quitTeamRequest, User loginUser) {
        //1.校验请求参数
        if(quitTeamRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2.校验队伍是否存在
        Long teamId = quitTeamRequest.getTeamId();
        if(teamId == null || teamId <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if(team == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }
        //3.校验我是否已加入队伍
        long userId = loginUser.getId();
        UserTeam team1 = new UserTeam();
        team1.setUserId(userId);
        team1.setTeamId(teamId);
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(team1);
        long count = userTeamService.count(queryWrapper);
        if(count == 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"未加入队伍");
        }
        //4.如果队伍
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        long countUser = userTeamService.count(queryWrapper);
        //i.只剩一人，队伍解散
        if(countUser == 1){
            //删除队伍
            this.removeById(teamId);
        }else {
            //ⅱ.还有其他人
            //.如果是队长退出队伍，权限转移给第二早加入的用户一先来后到（只用取id最小的2条数据）
            if(team.getUserId() == userId){
                queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("teamId", teamId);
                queryWrapper.last("order by id asc limit 2");
                List<UserTeam> list = userTeamService.list(queryWrapper);
                // todo list.size() > 2 ?
                if(CollectionUtils.isEmpty(list) || list.size() <= 1){
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                //更新队长id
                UserTeam userTeam = list.get(1);
                Long newUserId = userTeam.getUserId();
                Team newTeam = new Team();
                newTeam.setUserId(newUserId);
                newTeam.setId(teamId);
                boolean result = this.updateById(newTeam);
                if(!result){
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR,"更新队长失败");
                }
            }
        }
        //删除用户-队伍关系
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        queryWrapper.eq("userId", userId);
        return userTeamService.remove(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTeam(long teamId,User loginUser) {
        //1.校验请求参数
        if(teamId < 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2.校验队伍是否存在
        Team team = this.getById(teamId);
        if(team == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }
        //3.校验你是不是队伍的队长
        if(team.getUserId() != loginUser.getId()){
            throw new BusinessException(ErrorCode.NO_AUTH,"无访问权限");
        }
        //4.移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        boolean result = userTeamService.remove(queryWrapper);
        if(!result){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除队伍失败");
        }
        //5.删除队伍
        return this.removeById(teamId);
    }
}




